#include "Renderable.h"
#include <fstream>
#include <D3DX11.h>

Renderable::Renderable() : _fx(0), _IA(0)
{
	
}

Renderable::~Renderable()
{
	try
	{
		if (_fx)
			_fx->Release();
		if (_IA)
			_IA->Release();
	}
	catch(...)
	{
		MessageBoxA(0, "Exception thrown in Renderable destructor", 0, 0);
	}
}

bool Renderable::loadEffect(const char* filename, std::vector<char> &compiledShader) const
{

	std::ifstream fin(filename, std::ios::binary);
	if (!fin)
		return false;
	fin.seekg(0, std::ios_base::end);
	int size = (int)fin.tellg();

	compiledShader.resize(size);
	fin.seekg(0, std::ios_base::beg);
	fin.read(&compiledShader[0], size);
	if (!fin)
		return false;
	return true;

}

void Renderable::createEffect(const char* filename, ID3D11Device* _dxDev)
{
	std::vector<char> compiledShader(0);
	
	// shaders
	if (!loadEffect(filename, compiledShader))
		MessageBoxA(0, "Error Loading Effect", 0, 0);
	
	HRESULT hr = D3DX11CreateEffectFromMemory(&compiledShader[0], compiledShader.size(), 0, _dxDev, &_fx);
	if (FAILED(hr)) MessageBoxA(0, "Error creating FX", 0, 0);
	

}



void Renderable::createEffect(const char* filename, ID3D11Device* _dxDev, ID3DX11Effect** fx)
{
	std::vector<char> compiledShader(0);
	
	// shaders
	if (!loadEffect(filename, compiledShader))
		MessageBoxA(0, "Error Loading Effect", 0, 0);
	
	HRESULT hr = D3DX11CreateEffectFromMemory(&compiledShader[0], compiledShader.size(), 0, _dxDev, fx);
	if (FAILED(hr)) MessageBoxA(0, "Error creating FX", 0, 0);

}


HRESULT Renderable::createInputLayout(ID3D11Device* g_dev, const D3D11_INPUT_ELEMENT_DESC* elem_desc, unsigned int num_elements, const void* input_signature, SIZE_T length)
{
	return g_dev->CreateInputLayout(elem_desc, num_elements, input_signature, length, &_IA);
}

bool Renderable::createBuffer(ID3D11Device* g_dev, ID3D11Buffer** buffer, unsigned int size, D3D11_USAGE usage,
								unsigned int bind_flags, unsigned int cpu_flags,
								unsigned int misc_flags, unsigned int structuredByteStride, 
								const void* initial_data, unsigned int sysMemPitch, unsigned int sysMemSlicePitch)
{

	D3D11_BUFFER_DESC vbdesc;
	vbdesc.ByteWidth = size;
	vbdesc.BindFlags = bind_flags;
	vbdesc.CPUAccessFlags = cpu_flags;
	vbdesc.MiscFlags = misc_flags;
	vbdesc.Usage = usage;
	vbdesc.StructureByteStride = structuredByteStride;

	D3D11_SUBRESOURCE_DATA vbdata;
	vbdata.pSysMem = initial_data;
	vbdata.SysMemPitch = sysMemPitch;
	vbdata.SysMemSlicePitch = sysMemSlicePitch;

	HRESULT hr = g_dev->CreateBuffer(&vbdesc, &vbdata, buffer);
	if (FAILED(hr))
		return false;

	return true;

}
		


bool Renderable::createVertexBuffer(ID3D11Device* g_dev, ID3D11Buffer** buffer,   unsigned int size, D3D11_USAGE usage_flags,
								 bool bStreamOut, unsigned int cpu_flags, const void* initial_data)

{
	uint bind_flags = D3D11_BIND_VERTEX_BUFFER;
	
	if (bStreamOut)
		bind_flags |= D3D11_BIND_STREAM_OUTPUT;
	

	return createBuffer(g_dev, buffer, size, usage_flags, bind_flags, cpu_flags, 0, 0, initial_data, 0, 0);

}


bool Renderable::createTexture2D(ID3D11Device* g_dev, ID3D11Texture2D** tex, uint width,
								 uint height, DXGI_FORMAT format, uint bind_flags, uint arraySize, 
								 D3D11_USAGE usage_flags, uint cpu_flags, 
								 uint misc_flags, uint mipLevels, uint quality, uint count)
{

	D3D11_TEXTURE2D_DESC texdesc;
	texdesc.ArraySize = arraySize;
	texdesc.BindFlags = bind_flags;
	texdesc.CPUAccessFlags = cpu_flags;
	texdesc.Format = format;
	texdesc.Height = height;
	texdesc.MipLevels = mipLevels;
	texdesc.MiscFlags = misc_flags;
	texdesc.SampleDesc.Count = count;
	texdesc.SampleDesc.Quality = quality;
	texdesc.Usage = usage_flags;
	texdesc.Width = width;

	HRESULT hr = g_dev->CreateTexture2D(&texdesc, 0, tex);
	if (FAILED(hr))
		return false;

	return true;
}



bool Renderable::createTexture2D_SRV(ID3D11Device* g_dev, ID3D11Resource* res, 
									ID3D11ShaderResourceView** srv, DXGI_FORMAT format,
									uint mostDetailedMip,
									uint mipLevels)
{
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	srvdesc.Format = format;
	srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvdesc.Texture2D.MostDetailedMip = mostDetailedMip;
	srvdesc.Texture2D.MipLevels = mipLevels;

	HRESULT hr = g_dev->CreateShaderResourceView(res, &srvdesc, srv);
	if (FAILED(hr))
		return false;

	return true;
}

bool Renderable::createTexture2DArray_SRV(ID3D11Device* g_dev, ID3D11Resource* res, 
									ID3D11ShaderResourceView** srv, DXGI_FORMAT format,
									uint mostDetailedMip, 
									uint mipLevels, uint firstArraySlice, 
									uint arraySize)
{
	D3D11_SHADER_RESOURCE_VIEW_DESC srvdesc;
	srvdesc.Format = format;
	srvdesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
	srvdesc.Texture2DArray.ArraySize = arraySize;
	srvdesc.Texture2DArray.FirstArraySlice = firstArraySlice;
	srvdesc.Texture2DArray.MipLevels = mipLevels;
	srvdesc.Texture2DArray.MostDetailedMip = mostDetailedMip;
	
	HRESULT hr = g_dev->CreateShaderResourceView(res, &srvdesc, srv);
	if (FAILED(hr))
		return false;

	return true;
}



bool Renderable::createTexture2D_RTV(ID3D11Device* g_dev, ID3D11Resource* res, 
									ID3D11RenderTargetView** rtv, DXGI_FORMAT format,
									uint mipSlice)
{
	D3D11_RENDER_TARGET_VIEW_DESC rtvdesc;
	rtvdesc.Format = format;
	rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	rtvdesc.Texture2D.MipSlice = mipSlice;

	HRESULT hr = g_dev->CreateRenderTargetView(res, &rtvdesc, rtv);
	if (FAILED(hr))
		return false;

	return true;
}

bool Renderable::createTexture2DArray_RTV(ID3D11Device* g_dev, ID3D11Resource* res, 
											ID3D11RenderTargetView** rtv, DXGI_FORMAT format,
											uint mipSlice, 
											uint firstArraySlice, 
											uint arraySize)
{
	D3D11_RENDER_TARGET_VIEW_DESC rtvdesc;
	rtvdesc.Format = format;
	rtvdesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
	rtvdesc.Texture2DArray.ArraySize = arraySize;
	rtvdesc.Texture2DArray.FirstArraySlice = firstArraySlice;
	rtvdesc.Texture2DArray.MipSlice = mipSlice;

	HRESULT hr = g_dev->CreateRenderTargetView(res, &rtvdesc, rtv);
	if (FAILED(hr))
		return false;

	return true;
}

